package com.papper.common.service.base;

import com.google.common.collect.Maps;
import com.google.zxing.Binarizer;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType;
import com.google.zxing.LuminanceSource;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.Result;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.common.HybridBinarizer;
import com.papper.common.bean.base.BaseConstFile;
import com.papper.common.bean.base.BaseConstUrl;
import com.papper.common.cache.CacheConstants;
import com.papper.common.cache.redis.CacheClosureNullDefault;
import com.papper.common.config.PapperStorage;
import com.papper.common.enums.DataStatusEnum;
import com.papper.common.exception.ServiceException;
import com.papper.common.mapper.base.BaseConstFileMapper;
import com.papper.common.service.ServiceImpl;
import com.papper.common.utils.encrypt.Md5Tool;
import com.papper.common.utils.tool.DateTool;
import com.papper.common.utils.tool.OrderTool;
import com.papper.common.utils.tool.QRCodeTool;
import com.papper.common.utils.tool.StringTool;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import tk.mybatis.mapper.entity.Example;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Date;
import java.util.Map;
import java.util.zip.CRC32;

/**
 * The type Base const file service.
 *
 * @Author:LaJiao
 * @Date:2022-03-29 10 :06:49
 * @Description:文件资源
 */
@Service("baseConstFileService")
public class BaseConstFileService extends ServiceImpl<BaseConstFileMapper, BaseConstFile> {

    @Autowired
    BaseConstUrlService baseConstUrlService;

    @Override
    protected void clearCaches(BaseConstFile entity) {
        super.clearCaches(entity);
        cache.delete(CacheConstants.Keys(getModel(), entity.getResourceMd5()));
        cache.delete(CacheConstants.Keys(getModel(), "URL", entity.getResourceUrl()));
    }

    /**
     * Find by md 5 base const file.
     *
     * @param resourceMd5 the resource md 5
     * @return the base const file
     */
    public BaseConstFile findByMd5(final String resourceMd5) {
        return cache.getAndSet(new CacheClosureNullDefault<BaseConstFile>() {
            @Override
            public String getKey() {
                return CacheConstants.Keys(getModel(), resourceMd5);
            }

            @Override
            public BaseConstFile getValue() {
                Example          example  = new Example(BaseConstFile.class);
                Example.Criteria criteria = example.createCriteria();
                criteria.andEqualTo("resourceMd5", resourceMd5);
                return findOne(example);
            }

            @Override
            public Class<BaseConstFile> getTypeReference() {
                return BaseConstFile.class;
            }
        });
    }

    /**
     * Find by url base const file.
     *
     * @param resourceUrl the resource url
     * @return the base const file
     */
    public BaseConstFile findByUrl(final String resourceUrl) {
        return cache.getAndSet(new CacheClosureNullDefault<BaseConstFile>() {
            @Override
            public String getKey() {
                return CacheConstants.Keys(getModel(), "URL", resourceUrl);
            }

            @Override
            public BaseConstFile getValue() {
                Example          example  = new Example(BaseConstFile.class);
                Example.Criteria criteria = example.createCriteria();
                criteria.andEqualTo("resourceUrl", resourceUrl);
                return findOne(example);
            }

            @Override
            public Class<BaseConstFile> getTypeReference() {
                return BaseConstFile.class;
            }
        });
    }

    /**
     * Generate base const file.
     *
     * @param file  the file
     * @param type  the type
     * @param model the model
     * @param desc  the desc
     * @return the base const file
     * @throws ServiceException the service exception
     */
    public BaseConstFile generate(byte[] file, String type, String model, String desc) throws ServiceException {
        if (file == null || file.length <= 0) {
            logger.debug("save file resource is empty:[{}]", file);
            throw new ServiceException("501", "保存文件为空");
        }
        CRC32 crc32 = new CRC32();
        crc32.update(file);
        // 是否存在图片
        String        resourceMd5  = Md5Tool.getMd5(crc32.getValue() + type + model);
        BaseConstFile fileResource = findByMd5(resourceMd5);
        if (fileResource != null) {
            return fileResource;
        }
        // 生成目录
        Map<String, String> rtn      = Maps.newHashMap();
        StringBuilder       filePath = new StringBuilder();
        StringBuilder       httpPath = new StringBuilder();
        Date                now      = new Date();
        filePath.append(StringTool.nullToEmpty(PapperStorage.SystemConfig("systemFilePicPath")));
        if (!filePath.toString().endsWith("/")) {
            filePath.append("/");
        }
        String addPath = DateTool.formatDate(now, "yyyy/MM/dd");
        filePath.append(addPath).append("/").append(model);
        File myFilePath = new File(filePath.toString());
        if (!myFilePath.exists()) {
            myFilePath.mkdirs();
        }
        String imgFileName = resourceMd5.toLowerCase() + "." + type;
        filePath.append("/").append(imgFileName);
        logger.debug("save pic filepath:[{}]", filePath.toString());
        rtn.put("filePath", filePath.toString());
        // 生成访问路径
        httpPath.append(StringTool.nullToEmpty(PapperStorage.SystemConfig("systemFilePicHttp")));
        if (!httpPath.toString().endsWith("/")) {
            httpPath.append("/");
        }
        httpPath.append(addPath).append("/").append(model);
        httpPath.append("/").append(imgFileName);
        rtn.put("httpPath", httpPath.toString());
        // 生成文件
        OutputStream out = null;
        try {
            out = new FileOutputStream(filePath.toString());
            out.write(file);
            out.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            throw new ServiceException("502", "保存文件异常");
        } catch (IOException e) {
            e.printStackTrace();
            throw new ServiceException("503", "保存文件异常");
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        // 添加系统配置
        fileResource = new BaseConstFile();
        fileResource.setResourceMd5(resourceMd5);
        fileResource.setResourceCrc(String.valueOf(crc32.getValue()));
        fileResource.setResourceType(model);
        fileResource.setResourcePath(filePath.toString());
        fileResource.setResourceUrl(httpPath.toString());
        fileResource.setResourceSize(String.valueOf(file.length));
        fileResource.setResourceDesc(desc);
        fileResource.setDataStatus(DataStatusEnum.OPEN.getCode());
        save(fileResource);
        return fileResource;
    }

    /**
     * Generate with name base const file.
     *
     * @param name  the name
     * @param file  the file
     * @param type  the type
     * @param model the model
     * @param desc  the desc
     * @return the base const file
     * @throws ServiceException the service exception
     */
    public BaseConstFile generateWithName(String name, byte[] file, String type, String model, String desc) throws ServiceException {
        if (file == null || file.length <= 0) {
            logger.debug("save file resource is empty:[{}]", file);
            throw new ServiceException("501", "保存文件为空");
        }
        CRC32 crc32 = new CRC32();
        crc32.update(file);
        // 是否存在图片
        String        resourceMd5  = Md5Tool.getMd5(crc32.getValue() + type + model);
        BaseConstFile fileResource = findByMd5(resourceMd5);
        if (fileResource != null) {
            return fileResource;
        }
        // 生成目录
        Map<String, String> rtn      = Maps.newHashMap();
        StringBuilder       filePath = new StringBuilder();
        StringBuilder       httpPath = new StringBuilder();
        Date                now      = new Date();
        filePath.append(StringTool.nullToEmpty(PapperStorage.SystemConfig("systemFilePicPath")));
        if (!filePath.toString().endsWith("/")) {
            filePath.append("/");
        }
        String addPath = DateTool.formatDate(now, "yyyy/MM/dd");
        filePath.append(addPath).append("/").append(model);
        File myFilePath = new File(filePath.toString());
        if (!myFilePath.exists()) {
            myFilePath.mkdirs();
        }
        String imgFileName = name + "." + type;
        filePath.append("/").append(imgFileName);
        logger.debug("save pic filepath:[{}]", filePath.toString());
        rtn.put("filePath", filePath.toString());
        // 生成访问路径
        httpPath.append(StringTool.nullToEmpty(PapperStorage.SystemConfig("systemFilePicHttp")));
        if (!httpPath.toString().endsWith("/")) {
            httpPath.append("/");
        }
        httpPath.append(addPath).append("/").append(model);
        httpPath.append("/").append(imgFileName);
        rtn.put("httpPath", httpPath.toString());
        // 生成文件
        OutputStream out = null;
        try {
            out = new FileOutputStream(filePath.toString());
            out.write(file);
            out.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            throw new ServiceException("502", "保存文件异常");
        } catch (IOException e) {
            e.printStackTrace();
            throw new ServiceException("503", "保存文件异常");
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        // 添加系统配置
        fileResource = new BaseConstFile();
        fileResource.setResourceMd5(resourceMd5);
        fileResource.setResourceCrc(String.valueOf(crc32.getValue()));
        fileResource.setResourceType(model);
        fileResource.setResourcePath(filePath.toString());
        fileResource.setResourceUrl(httpPath.toString());
        fileResource.setResourceSize(String.valueOf(file.length));
        fileResource.setResourceDesc(desc);
        fileResource.setDataStatus(DataStatusEnum.OPEN.getCode());
        save(fileResource);
        return fileResource;
    }

    /**
     * Generate rr code base const file.
     *
     * @param content   the content
     * @param title     the title
     * @param imgSrc    the img src
     * @param addHeight the add height
     * @param desc      the desc
     * @return the base const file
     * @throws ServiceException the service exception
     */
    public BaseConstFile generateRrCode(String content, String title, String imgSrc, int addHeight, String desc) throws ServiceException {
        String model = "qrcode";
        if (StringTool.isEmpty(content)) {
            throw new ServiceException("501", "保存文件为空");
        }
        String        resourceMd5  = Md5Tool.getMd5(model + StringTool.nullToEmpty(content) + StringTool.nullToEmpty(title) + StringTool.nullToEmpty(imgSrc));
        BaseConstFile fileResource = findByMd5(resourceMd5);
        if (fileResource != null) {
            return fileResource;
        }
        Map<String, String> rtn      = Maps.newHashMap();
        StringBuilder       filePath = new StringBuilder();
        StringBuilder       httpPath = new StringBuilder();
        // 生成目录
        Date now = new Date();
        filePath.append(StringTool.nullToEmpty(PapperStorage.SystemConfig("systemFilePicPath")));
        if (!filePath.toString().endsWith("/")) {
            filePath.append("/");
        }
        filePath.append(model).append("/");
        String addPath = DateTool.formatDate(now, "yyyy/MM/dd");
        filePath.append(addPath);
        File myFilePath = new File(filePath.toString());
        if (!myFilePath.exists()) {
            myFilePath.mkdirs();
        }
        String imgFileName = resourceMd5.toLowerCase() + ".png";
        filePath.append("/").append(imgFileName);
        logger.debug("save pic filepath:[{}]", filePath.toString());
        rtn.put("filePath", filePath.toString());
        // 生成访问路径
        httpPath.append(StringTool.nullToEmpty(PapperStorage.SystemConfig("systemFilePicHttp")));
        if (!httpPath.toString().endsWith("/")) {
            httpPath.append("/");
        }
        httpPath.append(model).append("/").append(addPath);
        httpPath.append("/").append(imgFileName);
        rtn.put("httpPath", httpPath.toString());
        // 生成文件
        if (content.length() > 120) {
            String       code     = OrderTool.getUUID();
            BaseConstUrl shortUrl = baseConstUrlService.addInfo(code, content);
            if (shortUrl == null) {
                throw new ServiceException("502", "短链接生成失败");
            }
            StringBuilder shortUrlValue = new StringBuilder();
            shortUrlValue.append(PapperStorage.Config("SystemConfig", "short.url.domain", null));
            shortUrlValue.append("/short/");
            shortUrlValue.append(code);
            shortUrlValue.append(".htm");
            content = shortUrlValue.toString();
        }
        File imgFile = new File(filePath.toString());
        try {
            QRCodeTool.createCodeToFile(content, imgFile);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException("504", "图片文件写入异常");
        }
        // 添加系统配置
        fileResource = new BaseConstFile();
        fileResource.setResourceMd5(resourceMd5);
        fileResource.setResourceCrc(resourceMd5);
        fileResource.setResourceType(model);
        fileResource.setResourcePath(filePath.toString());
        fileResource.setResourceUrl(httpPath.toString());
        fileResource.setResourceSize(String.valueOf(imgFile.length()));
        fileResource.setResourceDesc(desc);
        fileResource.setDataStatus(DataStatusEnum.OPEN.getCode());
        save(fileResource);
        return fileResource;
    }

    /**
     * Generate rr code with price base const file.
     *
     * @param content   the content
     * @param price     the price
     * @param title     the title
     * @param addHeight the add height
     * @param desc      the desc
     * @return the base const file
     * @throws ServiceException the service exception
     */
    public BaseConstFile generateRrCodeWithPrice(String content, String price, String title, int addHeight, String desc) throws ServiceException {
        String model = "qrcode";
        if (StringTool.isEmpty(content)) {
            throw new ServiceException("501", "保存文件为空");
        }
        String        resourceMd5  = Md5Tool.getMd5(model + StringTool.nullToEmpty(content) + StringTool.nullToEmpty(title) + StringTool.nullToEmpty(price));
        BaseConstFile fileResource = findByMd5(resourceMd5);
        if (fileResource != null) {
            return fileResource;
        }
        Map<String, String> rtn      = Maps.newHashMap();
        StringBuilder       filePath = new StringBuilder();
        StringBuilder       httpPath = new StringBuilder();
        // 生成目录
        Date now = new Date();
        filePath.append(StringTool.nullToEmpty(PapperStorage.SystemConfig("systemFilePicPath")));
        if (!filePath.toString().endsWith("/")) {
            filePath.append("/");
        }
        filePath.append(model).append("/");
        String addPath = DateTool.formatDate(now, "yyyy/MM/dd");
        filePath.append(addPath);
        File myFilePath = new File(filePath.toString());
        if (!myFilePath.exists()) {
            myFilePath.mkdirs();
        }
        String imgFileName = resourceMd5.toLowerCase() + ".png";
        filePath.append("/").append(imgFileName);
        logger.debug("save pic filepath:[{}]", filePath.toString());
        rtn.put("filePath", filePath.toString());
        // 生成访问路径
        httpPath.append(StringTool.nullToEmpty(PapperStorage.SystemConfig("systemFilePicHttp")));
        if (!httpPath.toString().endsWith("/")) {
            httpPath.append("/");
        }
        httpPath.append(model).append("/").append(addPath);
        httpPath.append("/").append(imgFileName);
        rtn.put("httpPath", httpPath.toString());
        // 生成文件
        if (content.length() > 120) {
            String       code     = OrderTool.getUUID();
            BaseConstUrl shortUrl = baseConstUrlService.addInfo(code, content);
            if (shortUrl == null) {
                throw new ServiceException("502", "短链接生成失败");
            }
            StringBuilder shortUrlValue = new StringBuilder();
            shortUrlValue.append(PapperStorage.Config("SystemConfig", "short.url.domain", null));
            shortUrlValue.append("/short/");
            shortUrlValue.append(code);
            shortUrlValue.append(".htm");
            content = shortUrlValue.toString();
        }
        // 二维码
        //添加金额
        //添加文案
        File imgFile = new File(filePath.toString());
        try {
            QRCodeTool.createCodeToFile(content, imgFile);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException("504", "图片文件写入异常");
        }
        // 添加系统配置
        fileResource = new BaseConstFile();
        fileResource.setResourceMd5(resourceMd5);
        fileResource.setResourceCrc(resourceMd5);
        fileResource.setResourceType(model);
        fileResource.setResourcePath(filePath.toString());
        fileResource.setResourceUrl(httpPath.toString());
        fileResource.setResourceSize(String.valueOf(imgFile.length()));
        fileResource.setResourceDesc(desc);
        fileResource.setDataStatus(DataStatusEnum.OPEN.getCode());
        save(fileResource);
        return fileResource;
    }

    /**
     * Parse qrcode string.
     *
     * @param filePath the file path
     * @return the string
     */
    public String parseQrcode(String filePath) {
        try {
            File                        file         = new File(filePath);
            BufferedImage               image        = ImageIO.read(file);
            LuminanceSource             source       = new BufferedImageLuminanceSource(image);
            Binarizer                   binarizer    = new HybridBinarizer(source);
            BinaryBitmap                binaryBitmap = new BinaryBitmap(binarizer);
            Map<DecodeHintType, Object> hints        = Maps.newHashMap();
            hints.put(DecodeHintType.CHARACTER_SET, "UTF-8");
            MultiFormatReader formatReader = new MultiFormatReader();
            Result            result       = formatReader.decode(binaryBitmap, hints);
            return result.getText();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}
